// filehdr.cc 
//	Routines for managing the disk file header (in UNIX, this
//	would be called the i-node).
//
//	The file header is used to locate where on disk the 
//	file's data is stored.  We implement this as a fixed size
//	table of pointers -- each entry in the table points to the 
//	disk sector containing that portion of the file data
//	(in other words, there are no indirect or doubly indirect 
//	blocks). The table size is chosen so that the file header
//	will be just big enough to fit in one disk sector, 
//
//      Unlike in a real system, we do not keep track of file permissions, 
//	ownership, last modification date, etc., in the file header. 
//
//	A file header can be initialized in two ways:
//	   for a new file, by modifying the in-memory data structure
//	     to point to the newly allocated data blocks
//	   for a file already on disk, by reading the file header from disk
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"

#include "system.h"
#include "filehdr.h"

//----------------------------------------------------------------------
// FileHeader::Allocate
// 	Initialize a fresh file header for a newly created file.
//	Allocate data blocks for the file out of the map of free disk blocks.
//	Return FALSE if there are not enough free blocks to accomodate
//	the new file.
//
//	"freeMap" is the bit map of free disk sectors
//	"fileSize" is the bit map of free disk sectors
//----------------------------------------------------------------------

bool
FileHeader::Allocate(BitMap *freeMap, int fileSize)
{ 
    numBytes = fileSize;
    numSectors  = divRoundUp(fileSize, SectorSize);
    
    printf("\nStart allocating space for loading a file.\n\n");
    printf("File Size = %d bytes requires %d sectors.\n", numBytes, numSectors);

    if (numBytes > MaxFileSize)
    {
    	printf("\nFile size too large.\n");
    	return FALSE;
    }
    
    if (freeMap->NumClear() < numSectors)
    {
    	printf("\nNot enough sectors to allocate.\n");
    	return FALSE;
    }
    
	if (numBytes <= SmallFileSize)
	{
	    for (int i = 0; i < numSectors; i++)
			dataSectors[i] = freeMap->Find();
		
		printf("Allocated %d direct pointers.\n\n", numSectors);	
	}
	
	else if (numBytes <= MediumFileSize)
	{
		for (int i = 0; i < 61; i++)
			dataSectors[i] = freeMap->Find();
			
		dataSectors[61] = AllocateIndirectPointer(freeMap, numSectors - 61);
		
		printf("Allocated 61 direct pointers and ");
		printf("1 indirect pointer containing %d more direct pointers.\n\n", numSectors - 61);	
	}
						
	else
	{
		for (int i = 0; i < 60; i++)
			dataSectors[i] = freeMap->Find();
					
		dataSectors[60] = AllocateIndirectPointer(freeMap, numSectors - 60);
		dataSectors[61] = AllocateDoubleIndirectPointer(freeMap, numSectors - 124);
		
		printf("Allocated 60 direct pointers, ");
		printf("1 indirect pointer containing 64 more direct pointers, and ");
		printf("1 double indirect pointers containing %d indirect pointers which contain ", divRoundUp(numSectors - 124, 64));
		printf("%d direct pointers.\n\n", numSectors - 124);	
	}	
	
    return TRUE;
}

//----------------------------------------------------------------------
// FileHeader::AllocateIndirectPointer
//
//----------------------------------------------------------------------

int
FileHeader::AllocateIndirectPointer(BitMap *bitMap, int remainingSectors)
{
	if (remainingSectors > 64)
		remainingSectors = 64;
		
	int *directPointers = new int[64];
	for (int i = 0; i < remainingSectors; i++)
		directPointers[i] = bitMap->Find();

	int containingSector = bitMap->Find();
	WriteSectorPointer(containingSector, directPointers);	
		
	delete [] directPointers;
		
	return containingSector;		
}

//----------------------------------------------------------------------
// FileHeader::AllocateDoubleIndirectPointer
//
//----------------------------------------------------------------------

int
FileHeader::AllocateDoubleIndirectPointer(BitMap *bitMap, int remainingSectors)
{
	int *indirectPointers = new int[64];
	for (int i = 0; i < divRoundUp(remainingSectors, 64); i++)
		indirectPointers[i] = AllocateIndirectPointer(bitMap, remainingSectors - (i * 64));
		
	int containingSector = bitMap->Find();
	WriteSectorPointer(containingSector, indirectPointers);
	
	delete [] indirectPointers;
	
	return containingSector;
}

//----------------------------------------------------------------------
// FileHeader::Deallocate
// 	De-allocate all the space allocated for data blocks for this file.
//
//	"freeMap" is the bit map of free disk sectors
//----------------------------------------------------------------------

void 
FileHeader::Deallocate(BitMap *freeMap)
{
	if (numBytes <= SmallFileSize)
	{
	    for (int i = 0; i < numSectors; i++) 
	    {
			ASSERT(freeMap->Test((int) dataSectors[i]));  // ought to be marked!
			freeMap->Clear((int) dataSectors[i]);
	    }
	}
	
	else
	{		
		if (numBytes <= MediumFileSize)
		{
			for (int i = 0; i < 61; i++) 
		    {
				ASSERT(freeMap->Test((int) dataSectors[i]));  
				freeMap->Clear((int) dataSectors[i]);
		    }
		
			DeallocateIndirectPointer(freeMap, dataSectors[61], numSectors - 61);
		}		

		else
		{
			for (int i = 0; i < 60; i++) 
		    {
				ASSERT(freeMap->Test((int) dataSectors[i]));  // ought to be marked!
				freeMap->Clear((int) dataSectors[i]);
		    }
		    			
			DeallocateIndirectPointer(freeMap, dataSectors[60], numSectors - 60);
			DeallocateDoubleIndirectPointer(freeMap, dataSectors[61], numSectors - 124);
		}	
	}
}

//----------------------------------------------------------------------
// FileHeader::DeallocateIndirectPointer
//
//----------------------------------------------------------------------

void
FileHeader::DeallocateIndirectPointer(BitMap *bitMap, int containingSector, int remainingSectors)
{
	if (remainingSectors > 64)
		remainingSectors = 64;

	int *directPointers = new int[64];
	ReadSectorPointer(containingSector, directPointers);
			
	for (int i = 0; i < remainingSectors; i++)
	{
		ASSERT(bitMap->Test((int)directPointers[i]));
		bitMap->Clear((int)directPointers[i]);
	}

	delete [] directPointers;
	
	ASSERT(bitMap->Test(containingSector));
	bitMap->Clear(containingSector);
}

//----------------------------------------------------------------------
// FileHeader::DeallocateDoubleIndirectPointer
//
//----------------------------------------------------------------------

void
FileHeader::DeallocateDoubleIndirectPointer(BitMap *bitMap, int containingSector, int remainingSectors)
{
	int *indirectPointers = new int[64];
	ReadSectorPointer(containingSector, indirectPointers);
			
	for (int i = 0; i < divRoundUp(remainingSectors, 64); i++)
		DeallocateIndirectPointer(bitMap, indirectPointers[i], remainingSectors - (i * 64));

	delete [] indirectPointers;
	
	ASSERT(bitMap->Test(containingSector));
	bitMap->Clear(containingSector);
}

//----------------------------------------------------------------------
// FileHeader::FetchFrom
// 	Fetch contents of file header from disk. 
//
//	"sector" is the disk sector containing the file header
//----------------------------------------------------------------------

void
FileHeader::FetchFrom(int sector)
{
    synchDisk->ReadSector(sector, (char *)this);
}

//----------------------------------------------------------------------
// FileHeader::WriteBack
// 	Write the modified contents of the file header back to disk. 
//
//	"sector" is the disk sector to contain the file header
//----------------------------------------------------------------------

void
FileHeader::WriteBack(int sector)
{
    synchDisk->WriteSector(sector, (char *)this); 
}

//----------------------------------------------------------------------
// FileHeader::ReadSectorPointer
//
//----------------------------------------------------------------------

void 
FileHeader::ReadSectorPointer(int sector, int *pointer)
{
	unsigned char data[SectorSize];
	synchDisk->ReadSector(sector, (char *)data);
	
	for (int i = 0; i < SectorSize / 4; i++)
		pointer[i] = (int)*(data + (i * 4));
}

//----------------------------------------------------------------------
// FileHeader::WriteSectorPointer
//
//----------------------------------------------------------------------

void
FileHeader::WriteSectorPointer(int sector, int *pointer)
{
	unsigned char *data = (unsigned char *) pointer; 
	
	synchDisk->WriteSector(sector, (char *)data);	
}

//----------------------------------------------------------------------
// FileHeader::ByteToSector
// 	Return which disk sector is storing a particular byte within the file.
//      This is essentially a translation from a virtual address (the
//	offset in the file) to a physical address (the sector where the
//	data at the offset is stored).
//
//	"offset" is the location within the file of the byte in question
//----------------------------------------------------------------------

int
FileHeader::ByteToSector(int offset)
{
	ASSERT(!(offset < 0 || offset > MaxFileSize));
    
	int sectorNumber = offset / SectorSize;
	
	if (numBytes <= SmallFileSize) // SMALL FILE
		return dataSectors[sectorNumber];
	
	else if (numBytes <= MediumFileSize) // MEDIUM FILE
	{
		if (sectorNumber >= 61) // INDIRECT POINTER
			return IndirectToSector(dataSectors[61], sectorNumber - 62);
		
		else // DIRECT POINTER
			return dataSectors[sectorNumber];
	}

	else // LARGE FILE
	{
		if (sectorNumber >= 124) // DOUBLE INDIRECT POINTER
			return DoubleIndirectToSector(dataSectors[NumDirect - 1], sectorNumber - 125);
		
		else if (sectorNumber >= 60) // INDIRECT POINTER
			return IndirectToSector(dataSectors[60], sectorNumber - 61);
		
		else // DIRECT POINTER
			return dataSectors[sectorNumber];		
	}	 
}

//----------------------------------------------------------------------
// FileHeader::IndirectToSector
//----------------------------------------------------------------------

int 
FileHeader::IndirectToSector(int indirectSector, int sectorNum)
{
	int *directPointers = new int[64];
	ReadSectorPointer(indirectSector, directPointers);
	
	int sector = directPointers[sectorNum];
	
	delete [] directPointers;
	
	return sector;
}

//----------------------------------------------------------------------
// FileHeader::DoubleIndirectToSector
//----------------------------------------------------------------------

int 
FileHeader::DoubleIndirectToSector(int doubleIndirectSector, int sectorNum)
{
	int *indirectPointers = new int[64];
	ReadSectorPointer(doubleIndirectSector, indirectPointers);
	
	int sector = IndirectToSector(indirectPointers[sectorNum / 64], sectorNum % 64);
	
	delete [] indirectPointers;
	
	return sector;
}

//----------------------------------------------------------------------
// FileHeader::FileLength
// 	Return the number of bytes in the file.
//----------------------------------------------------------------------

int
FileHeader::FileLength()
{
    return numBytes;
}

//----------------------------------------------------------------------
// FileHeader::Print
// 	Print the contents of the file header, and the contents of all
//	the data blocks pointed to by the file header.
//----------------------------------------------------------------------

void
FileHeader::Print()
{
    int i, j, k;
    char *data = new char[SectorSize];

    printf("FileHeader contents.  File size: %d.  File blocks:\n", numBytes);
    for (i = 0; i < numSectors; i++)
		printf("%d ", dataSectors[i]);
		
    printf("\nFile contents:\n");
    for (i = k = 0; i < numSectors; i++) 
    {
		synchDisk->ReadSector(dataSectors[i], data);
		
        for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) 
        {
	    	if ('\040' <= data[j] && data[j] <= '\176')  
				printf("%c", data[j]);
            else
				printf("\\%x", (unsigned char)data[j]);
		}
		
        printf("\n"); 
    }
    
    delete [] data;
}
